﻿using Exiled.API.Features;
using Exiled.API.Features.Pools;
using System;
using System.Collections.Generic;
using System.Linq;

namespace RgCore.Menu
{
    public interface IDynamicMenu
    {
        string Title { get; }
        string DisplayText { get; }
    }

    public delegate bool ConditionResponse(out string response);

    public delegate bool InstanceConditionResponse<T>(T instance, out string response) where T : class;

    public abstract class MenuElement
    {
        public MenuElement(Page parent, string elementid, bool disabled)
        {
            Parent = parent;
            ElementId = elementid;
            Disabled = disabled;
        }

        public Page Parent { get; }
        public string ElementId { get; }
        public bool Disabled { get; set; }

        public MenuElement Clone()
        {
            MenuElement me = (MenuElement)MemberwiseClone();
            return me;
        }
    }

    public abstract class FunctionalElement : MenuElement
    {
        public FunctionalElement(Page parent, string elementid, string title, Func<Player, string> display, InstanceConditionResponse<Player> response, bool disabled) : base(parent, elementid, disabled)
        {
            Condition = response;
            Title = title;
            DisplayText = display;
            if (DisplayText == null)
            {
                DisplayText = (Player p) => Title;
            }
            if (Condition == null)
                Condition = new InstanceConditionResponse<Player>((Player _, out string a) => { a = string.Empty; return true; });
        }

        public InstanceConditionResponse<Player> Condition { get; }
        public string Title { get; }
        public Func<Player, string> DisplayText { get; }
        public bool AwaitRequired { get; set; }
    }

    public class LineBreak : MenuElement
    {
        public LineBreak(Page parent) : base(parent, string.Empty, false)
        {
        }
    }

    public class Page : FunctionalElement
    {
        public const int MaxChoicesPerPage = 5;
        public const int MaxElementsPerPage = 8;

        public Page(Page parent, string elementid, string title, Func<Player, string> display, InstanceConditionResponse<Player> response, Func<Player, MenuElement[]> getchildren, bool disabled) : base(parent, elementid, title, display, response, disabled)
        {
            Children = getchildren;
            if (getchildren == null)
            {
                Children = (_) => MenuInterface.RegisteredElements.Where(p => p.Parent == this).ToArray();
            }
        }

        public Func<Player, MenuElement[]> Children { get; set; }

        public List<MenuElement[]> PagedChildren(Player player)
        {
            List<MenuElement[]> list = new List<MenuElement[]>();
            List<MenuElement> page = ListPool<MenuElement>.Pool.Get();
            int choice = 0;
            var elements = Children(player);
            foreach (MenuElement me in elements)
            {
                if (me is LineBreak)
                {
                    if (page.Count > 0)
                    {
                        list.Add(page.ToArray());
                        page.Clear();
                        choice = 0;
                    }
                    continue;
                }
                if (me is FunctionalElement fe)
                {
                    if (choice >= MaxChoicesPerPage)
                    {
                        list.Add(page.ToArray());
                        page.Clear();
                        choice = 0;
                    }
                    choice++;
                    page.Add(me);
                }
                else if (me is Text)
                {
                    page.Add(me);
                }
                if (page.Count >= MaxElementsPerPage)
                {
                    list.Add(page.ToArray());
                    page.Clear();
                    choice = 0;
                }
            }
            if (page.Count > 0)
                list.Add(page.ToArray());
            ListPool<MenuElement>.Pool.Return(page);
            return list;
        }
    }

    public class Option : FunctionalElement
    {
        public Option(Page parent, string elementid, string title, Func<Player, string> display, InstanceConditionResponse<Player> response, Func<Player, string> trigger, bool disabled) : base(parent, elementid, title, display, response, disabled)
        {
            Trigger = trigger;
            if (trigger == null)
                Trigger = (_) => String.Empty;
        }

        public Func<Player, string> Trigger { get; }
        public bool DontRefresh { get; set; }
    }

    public class Text : MenuElement
    {
        public Text(Page parent, string elementid, Func<string> text, bool disabled) : base(parent, elementid, disabled)
        {
            Content = text;
        }

        public Func<string> Content { get; }
    }
}